package pri.damai.xiaowu.common.security.config;

import pri.damai.xiaowu.common.core.util.DateUtils;
import pri.damai.xiaowu.common.security.token.RuiRuanAccessToken;
import pri.damai.xiaowu.common.security.token.RuiRuanRefreshToken;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * Token 工具类
 * @Desc
 * @Author DaMai
 * @Date 2021/10/13 17:47
 * 但行好事，莫问前程。
 */
public class SecurityUtil {

    @Value("${xiaowu.security.jwt-secret:xiaowu}")
    String secret;
    @Value("${xiaowu.security.expiration:36000}")
    Long expiration;
    @Value("${xiaowu.security.refresh-expiration:-1}")
    Long refreshExpiration;
    @Value("${xiaowu.security.expiration-interval:3600}")
    Long expirationInterval;

    /**
     * 创建 RuiRuanAccessToken
     * @apiNote [附加描述]
     * @param claims 负载信息
     * @return com.ruiruan.security.token.RuiRuanAccessToken
     * @author DaMai
     * @date 2021/10/19 10:29
     */
    public RuiRuanAccessToken createAccessToken(Map<String, Object> claims, boolean isCreateRefreshToken) {

        // 创建 AccessToken
        Long expiration = getExpiration();

        Date date = createDate(expiration);
        LocalDateTime expireTime = DateUtils.asLocalDateTime(date);

        String token = Jwts.builder()
                .setClaims(claims)
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

        RuiRuanAccessToken accessToken = new RuiRuanAccessToken();
        accessToken.setToken(token)
                .setValidTime(expiration)
                .setExpireTime(expireTime);

        // 创建 RefreshToken
        if (isCreateRefreshToken) {
            Long refreshExpiration = getRefreshExpiration();
            Date refreshDate = createDate(refreshExpiration);
            LocalDateTime refreshLocalDate = DateUtils.asLocalDateTime(refreshDate);

            String refreshToken = Jwts.builder()
                    .setExpiration(refreshDate)
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();
            RuiRuanRefreshToken ruiRuanRefreshToken = new RuiRuanRefreshToken();
            ruiRuanRefreshToken.setRefreshToken(refreshToken)
                    .setValidTime(refreshExpiration)
                    .setExpireTime(refreshLocalDate);

            accessToken.setRefreshToken(ruiRuanRefreshToken);
        }
        return accessToken;
    }

    /**
     * 获取 Claims
     * @apiNote [附加描述]
     * @param token
     * @return io.jsonwebtoken.Claims
     * @author DaMai
     * @date 2021/7/15 11:12
     */
    public Claims getClaims(String token) throws JwtException {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 生成 token
     *
     * @param claims
     * @return java.lang.String
     * @author DaMai
     * @date 2020/11/16 10:23
     */
    @Deprecated
    public String createToken(Map<String, Object> claims, Date expirationDate) {

        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    /**
     * 生成 token
     *
     * @param claims
     * @param subject
     * @return java.lang.String
     * @author DaMai
     * @date 2020/11/16 10:25
     */
    @Deprecated
    public String createToken(Map<String, Object> claims, String subject, Date expirationDate) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    /**
     * 创建过期时间
     *
     * @return java.util.Date
     * @author DaMai
     * @date 2020/11/16 10:22
     */
    public Date createExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    public Date createRefreshExpirationDate() {
        return new Date(System.currentTimeMillis() + refreshExpiration * 1000);
    }

    /**
     * 创建 Token 时间
     * @apiNote [附加描述]
     * @param time
     * @return java.util.Date
     * @author DaMai
     * @date 2021/11/4 21:18
     */
    public Date createDate(Long time) {
        return new Date(System.currentTimeMillis() + time);
    }

    /**
     * 获取 Token 有效时间
     * @apiNote 单位：毫秒
     * @return java.lang.Long
     * @author DaMai
     * @date 2021/10/19 10:16
     */
    public Long getExpiration() {
        return expiration * 1000;
    }

    /**
     * 获取 refreshToken 有效时间
     * @apiNote 单位：毫秒
     * @return java.lang.Long
     * @author DaMai
     * @date 2021/10/19 10:16
     */
    public Long getRefreshExpiration() {
        if (Objects.equals(refreshExpiration, -1L)) {
            refreshExpiration = expiration * 2;
        }
        return refreshExpiration * 1000;
    }

    /**
     * 检查时间间隔
     * @apiNote [附加描述]
     * @param ruiRuanAccessToken token 实体
     * @return boolean
     * @author DaMai
     * @date 2021/12/7 15:14
     */
    public boolean isMoreThanTimeInterval(RuiRuanAccessToken ruiRuanAccessToken) {
        // 最后一次调用时间
        Long lastCallTime = ruiRuanAccessToken.getLastCallTime();
        LocalDateTime lastCallDateTime;
        if (Objects.isNull(lastCallTime)) {
            lastCallDateTime = LocalDateTime.now();
        } else {
            lastCallDateTime = DateUtils.asLocalDateTime(lastCallTime);
        }

        // 限制时间
        LocalDateTime nowDateTime = LocalDateTime.now();
        LocalDateTime limitDateTime = lastCallDateTime.plusSeconds(expirationInterval);

        return nowDateTime.isAfter(limitDateTime);
    }
}
